"use strict";

const admZip = require("adm-zip");
const fs = require("fs");
const ci = require("miniprogram-ci"); // 官方文档：https://developers.weixin.qq.com/miniprogram/dev/devtools/ci.html
const Service = require("egg").Service;
let project;
const showlog = false;

class WeminiService extends Service {
  constructor(ctx) {
    super(ctx);
    this.error;
    this.parmas;
    this.root = "../packages/miniprogram"; // 相关文件存储的根目录
    this.desc = "mnongclub";
    this.qrcodeFormat = "base64"; // image | base64

    process.on("uncaughtException", function (err) {
      // 打印出错误
      console.log(err);
      // 打印出错误的调用栈方便调试
      console.log(err.stack);
    });
  }
  async end() {}

  async upload(parmas) {
    await this._setParmas(parmas);
    return await this._upload();
  }

  async preview(parmas) {
    await this._setParmas(parmas);
    const res = await this._preview();
    res.base64 = await this._readfile(this.qrcodeOutputDest);
    return res;
  }

  async sourceMap(parmas) {
    await this._setParmas(parmas);
    await this._getDevSourceMap();
    return await this._readMap(this.sourceMapSavePath);
  }

  async _setParmas(parmas) {
    this.parmas = parmas;
    this.appid = parmas.appid;
    if (parmas.root) this.root = parmas.root;
    this.projectPath = `${this.root}/mp-weixin`;
    this.privateKeyPath = `${this.root}/${this.appid}_private.key`;
    this.qrcodeOutputDest = `${this.root}/${this.appid}_preview.jpg`;
    this.sourceMapSavePath = `${this.root}/${this.appid}_sourceMap.zip`;
    await this._createPrivateKey();
    this._buildProject();
  }

  async _buildProject() {
    project = new ci.Project({
      type: "miniProgram",
      appid: this.appid,
      projectPath: this.projectPath,
      privateKeyPath: this.privateKeyPath,
      ignores: ["node_modules/**/*"],
    });
  }

  async _createPrivateKey() {
    fs.writeFileSync(this.privateKeyPath, this.parmas.private, (error) => {
      if (error) console.log(error);
    });
  }

  async _upload() {
    return await ci.upload({
      project,
      version: this.parmas.version,
      desc: this.parmas.desc, // 此备注将显示在“小程序助手”开发版列表中
      setting: {
        es6: false, // 对应小程序开发者工具的 "es6 转 es5"
        es7: false, // 对应小程序开发者工具的 "增强编译"
        minifyJS: false, // 压缩 JS 代码
        minifyWXML: false, // 压缩 WXML 代码
        minify: false, // 压缩所有代码，对应小程序开发者工具的 "压缩代码"
        codeProtect: false, // 对应小程序开发者工具的 "代码保护",
        autoPrefixWXSS: false, // 对应小程序开发者工具的 "样式自动补全"
      },
      onProgressUpdate: this._onProgress,
      robot: 1, // 指定使用哪一个 ci 机器人，可选值：1 ~ 30
      threads: 1, // 指定本地编译过程中开启的线程数
    });
  }

  async _preview() {
    return await ci.preview({
      project,
      desc: this.desc,
      setting: {
        es6: false, // 对应小程序开发者工具的 "es6 转 es5"
        es7: false, // 对应小程序开发者工具的 "增强编译"
        minifyJS: false, // 压缩 JS 代码
        minifyWXML: false, // 压缩 WXML 代码
        minify: false, // 压缩所有代码，对应小程序开发者工具的 "压缩代码"
        codeProtect: false, // 对应小程序开发者工具的 "代码保护",
        autoPrefixWXSS: false, // 对应小程序开发者工具的 "样式自动补全"
      },
      onProgressUpdate: this._onProgress, // 进度更新监听函数
      robot: 1, // 指定使用哪一个 ci 机器人，可选值：1 ~ 30
      qrcodeFormat: this.qrcodeFormat, // 返回二维码文件的格式 "image" 或 "base64"， 默认值 "terminal" 供调试用
      qrcodeOutputDest: this.qrcodeOutputDest, // 二维码文件保存路径
      pagePath: "pages/index/index", // 预览页面
      searchQuery: "", // 预览参数 [注意!]这里的`&`字符在命令行中应写成转义字符`\&`，如a=1&b=2
      scene: 1011, // 默认值 1011，具体含义见场景值列表
    });
  }

  async _getDevSourceMap() {
    // ci.getDevSourceMap 无法捕获到错误信息
    return await ci.getDevSourceMap({
      project,
      robot: 1,
      sourceMapSavePath: this.sourceMapSavePath,
    });
  }

  async _readMap(path) {
    let content;
    const zip = new admZip(path);
    const zipEntries = zip.getEntries();
    zipEntries.forEach(function (entry) {
      if (entry.isDirectory === false) {
        const file = entry.entryName.toString().split("/").pop();
        if (file === "app-service.js.map") {
          content = JSON.parse(entry.getData().toString("utf8"));
        }
      }
    });
    return content;
  }

  async _readfile(path) {
    const readFile = require("util").promisify(fs.readFile);
    try {
      const fr = await readFile(path, "utf-8");
      return fr;
    } catch (err) {
      console.log("Error", err);
    }
  }

  _onProgress(message) {
    if (showlog) console.log(message);
  }
}

module.exports = WeminiService;
